﻿using FreeSql;
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Aspire.Repository.FreeSql
{
    public class RepositoryFreeSql<TEntity> : Repository<long, TEntity>, IRepositoryFreeSql<TEntity>
         where TEntity : BaseFreeSqlEntity
    {
        private readonly IFreeSql _freeSql;

        public RepositoryFreeSql(IFreeSql freeSql)
        {
            _freeSql = freeSql;
        }

        private static ISelect<TEntity> _query = default;
        public ISelect<TEntity> Query => _query == default ? _query = _freeSql.Queryable<TEntity>() : _query;

        private static string _tableName = default;
        public override string TableName
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_tableName)) return _tableName;

                Type tb = typeof(TEntity);
                object atr = tb.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(TableAttribute));
                if (atr == null)
                {
                    return _tableName = tb.FullName;
                }
                TableAttribute tba = (TableAttribute)atr;
                return _tableName = tba.Name;
            }
        }

        private static string _primaryKeyName = default;
        public override string PrimaryKeyName
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_primaryKeyName)) return _primaryKeyName;

                foreach ((ColumnAttribute col, PropertyInfo prop) in IteratorCol())
                {
                    if (col == null) continue; // 没有声明

                    if (!col.IsPrimary) continue; // 声明但不是主键声明

                    if (string.IsNullOrWhiteSpace(col.Name)) return _primaryKeyName = prop.Name; // 声明主键但没有声明别名

                    return _primaryKeyName = col.Name; // 声明主键且声明别名
                }
                return _primaryKeyName = string.Empty;
            }
        }

        private static string _identityName = default;
        public override string IdentityName
        {
            get
            {
                if (_identityName != default) return _identityName;

                foreach ((ColumnAttribute col, PropertyInfo prop) in IteratorCol())
                {
                    if (col == null) continue;
                    if (!col.IsIdentity) continue;
                    if (string.IsNullOrWhiteSpace(col.Name)) return _identityName = prop.Name; // 声明自增但没有声明别名
                    return _identityName = col.Name; // 声明自增且声明别名
                }
                return _identityName = string.Empty;
            }
        }


        private IEnumerable<(ColumnAttribute, PropertyInfo)> IteratorCol()
        {
            foreach (PropertyInfo prop in typeof(TEntity).GetProperties())
            {
                object atr = prop.GetCustomAttributes(false).FirstOrDefault(x => x.GetType() == typeof(ColumnAttribute));

                yield return (atr == null ? null : (ColumnAttribute)atr, prop);
            }
        }

        public override async Task<int> AddRangeAsync(IEnumerable<TEntity> entities)
            => await _freeSql.Insert(entities).ExecuteAffrowsAsync();

        public override async Task<long[]> AddRangeThenIdsAsync(IEnumerable<TEntity> entities)
        {
            List<long> ls = new List<long>();
            if (entities.Count() > 1)
            {
                _freeSql.Transaction(() =>
                {
                    foreach (TEntity entity in entities)
                    {
                        ls.Add(_freeSql.Insert(entities).ExecuteIdentity());
                    }
                });
            }
            else
            {
                ls.Add(_freeSql.Insert(entities).ExecuteIdentity());
            }
            return await Task.FromResult(ls.ToArray());
        }

        public override async Task<int> DeleteRangeAsync(IEnumerable<long> ids)
            => await _freeSql.Delete<TEntity>(ids).ExecuteAffrowsAsync();

        public override async Task<TEntity[]> GetByIdsAsync(IEnumerable<long> ids)
            => (await Query.Where(x => ids.Contains(x.Id)).ToListAsync()).ToArray();

        public override async Task<int> UpdateRangeAsync(IEnumerable<TEntity> entities)
            => await _freeSql.Update<TEntity>(entities).SetSource(SetUpdate(entities)).ExecuteAffrowsAsync();

        private IEnumerable<TEntity> SetUpdate(IEnumerable<TEntity> entities)
            => entities.Select(x =>
            {
                x.UpdatedAt = DateTime.Now;
                return x;
            });

        public override async Task<int> ExecuteSqlRawAsync(string sql, params object[] param)
          => await _freeSql.Ado.ExecuteNonQueryAsync(sql, param);

        public override async Task<TEntity[]> QuerySqlRawAsync(string sql, params object[] param)
            => (await _freeSql.Ado.QueryAsync<TEntity>(sql, param)).ToArray();

    }
}
